package it.polimi.comorazio;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;

import javax.xml.parsers.SAXParserFactory;

import org.osmdroid.tileprovider.util.CloudmadeUtil;
import org.osmdroid.views.overlay.PathOverlay;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.content.Context;
import android.graphics.Point;

public class BlueLine extends PathOverlay {
	String CMURL;
	private ArrayList<String> instructions = new ArrayList<String>();

	public BlueLine(int lineColor, Context ctx) {
		super(lineColor, ctx);
		getPaint().setStrokeWidth(5f);
		CloudmadeUtil.retrieveCloudmadeKey(ctx);
		// prebuild the URL
		// http://routes.cloudmade.com/YOUR-API-KEY-GOES-HERE/api/0.3/PARAMS-GO-HERE
		CMURL = "http://routes.cloudmade.com/"
				+ CloudmadeUtil.getCloudmadeKey() + "/api/0.3/";
	}

	double endLat, endLng;

	public void showDirectionsToPredefinedLocation(double startLat,
			double startLng, boolean shortest, String lang, boolean metric)
			throws Exception {
		showDirections(startLat, startLng, endLat, endLng, shortest, lang,
				metric);
	}

	/**
	 * Request directions from a point to another point. Line is drawn
	 * automatically in the draw method, need to just request directions and
	 * parse the XML into the List of Point's.
	 * 
	 * @param startLat
	 *            Latitude of starting point
	 * @param startLng
	 *            Longitude of starting point
	 * @param endLat
	 *            Latitude of ending point
	 * @param endLng
	 *            Longitude of ending point
	 * @param shortest
	 *            Whether select shortest or fastest route
	 * @param lang
	 *            2-char ISO code of the language of directions
	 * @param metric
	 *            Whether use metric or imperial system
	 * @throws Exception
	 */
	public void showDirections(double startLat, double startLng, double endLat,
			double endLng, boolean shortest, String lang, boolean metric)
			throws Exception {
		/*
		 * Query up the routing service to figure out the turn points
		 * documentation for cloudmade routing service
		 * http://developers.cloudmade.com/wiki/routing-http-api/Documentation
		 */
		// add PARAMS to the URL
		// start_point,[transit_point1,...,transit_pointN],end_point/route_type[/route_type_modifier].output_format[?lang=(Two
		// letter ISO 3166-1 code)][&units=(km|miles)]
		CMURL += startLat + "," + startLng + "," + endLat + "," + endLng
				+ "/foot/" + (shortest ? "shortest" : "fastest") + ".gpx?lang="
				+ lang + "&units=" + (metric ? "km" : "miles");
		// fetch directions and parse returned XML
		String directionsXML = getContent(CMURL, null, "UTF8");
		// parse XML
		TurnPointsParser parser = new TurnPointsParser();
		XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser()
				.getXMLReader();
		xmlReader.setContentHandler(parser);
		if (!"".equals(directionsXML)) {
			xmlReader.parse(new InputSource(new StringReader(directionsXML)));
		}
	}

	public class TurnPointsParser extends DefaultHandler {

		public static final String POINT_ELEMENT = "wpt";
		public static final String INSTRUCTION_ELEMENT = "desc";
		ArrayList<Point> tempPoints = new ArrayList<Point>();
		String tempVal;

		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes atts) throws SAXException {
			tempVal = "";
			if (qName.equals(POINT_ELEMENT)) {
				try {
					int late6 = degreesToMicrodegrees(Double.parseDouble(atts
							.getValue("lat")));
					int lnge6 = degreesToMicrodegrees(Double.parseDouble(atts
							.getValue("lon")));
					tempPoints.add(new Point(late6, lnge6));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			tempVal = new String(ch, start, length);
		}

		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			if (qName.equals(INSTRUCTION_ELEMENT)) {
				instructions.add(tempVal);
			}
		}

		@Override
		public void endDocument() {
			setPoints(tempPoints);
		}
	}

	void setPoints(ArrayList<Point> points) {
		clearPath();
		for (Point p : points) {
			addPoint(p.x, p.y);
		}
	}

	/**
	 * Fetch the content of service at the given URL with provided parameters.
	 * Also transcode the input into UTF-8 according to specified charset used
	 * by the service.
	 * 
	 * @param serviceUrl
	 * @param requestParams
	 * @param serviceCharset
	 * @return String with fetched content
	 * @throws Exception
	 */
	public static String getContent(String serviceUrl, String requestParams,
			String serviceCharset) throws Exception {
		URL service = new URL(serviceUrl);
		URLConnection serviceConnection = service.openConnection();
		serviceConnection.setDoOutput(true);
		// write request to the connection
		OutputStreamWriter request = new OutputStreamWriter(
				serviceConnection.getOutputStream());
		if (requestParams != null) {
			request.write(requestParams);
		}
		request.flush();
		// read returned output
		BufferedReader in = new BufferedReader(new InputStreamReader(
				serviceConnection.getInputStream(), serviceCharset));
		String inputLine;
		String returnedContent = "";
		while ((inputLine = in.readLine()) != null) {
			returnedContent += inputLine;
		}
		in.close();
		request.close();
		return returnedContent;
	}

	public static final double MICRODEGREES_COEFF = 1E6;

	public static int degreesToMicrodegrees(double degrees) {
		return (int) (degrees * MICRODEGREES_COEFF);
	}

	public static double microdegreesToDegrees(int microdegrees) {
		return (double) microdegrees / (double) MICRODEGREES_COEFF;
	}

	public void setEndLat(double endLat) {
		this.endLat = endLat;
	}

	public void setEndLng(double endLng) {
		this.endLng = endLng;
	}

	public ArrayList<String> getInstructions() {
		return instructions;
	}

}